#!/usr/bin/python


from python_interface_to_soprano import *
from ontology_manager import OntologyManager
import logging
logger = logging.getLogger("Pimo")


#**********************************************************#
# This class offers the possibilities from the PIMO Ontology. It can get much information from the nepomuk database, such as properties, objects to be displayed and chosen by the user.
class Pimo:
	
	# @param interface_to_soprano an interface to call the methods : Interface
	def __init__(self, interface_to_soprano):
		
		self.i = interface_to_soprano
		self.prec_ontologies = set()
		self.prec_elements = set()


               ##############################               
########################## GETTERS #########################
               ##############################               


	def getDomainsArray(self, property_uri):
		
		query = "SELECT ?dom WHERE { <" + property_uri + "> <" + RDFS_DOMAIN + "> ?dom }"
		domains = self.i.executeQuery(query)
		
		domains_res = []
		while domains.next():
			domain = domains.current()[0]["dom"][1]
			domains_res.append(domain)
			
		domains.close()
		
		return domains_res

############################################################

	def getDomainTypesOf(self, property_uri):
		
		domains = self.getDomainsArray(property_uri)
		
		return domains
		
############################################################

	# This function returns the types defined in the database as the ranges for the given property.
	def getRangesArray(self, property_uri):
		
		query = "SELECT ?ran WHERE { <" + property_uri + "> <" + RDFS_RANGE + "> ?ran }"
		ranges = self.i.executeQuery(query)
		
		ranges_res = []
		while ranges.next():
			my_range = ranges.current()[0]["ran"][1]
			ranges_res.append(my_range)
		
		ranges.close()
		return ranges_res

############################################################

	# This function looks at the elements used as range elements for the given property, then retrieves their type and returns these types as "usage ranges".
	# This method can be called, typically, when no "official" range are defined (getRangesArray() returns a void list).
	def getUsageRangesArray(self, property_uri):
		
		query = "SELECT ?dom_elt ?ran_elt WHERE { ?dom_elt <" + property_uri + "> ?ran_elt }"
		ranges_elts = self.i.executeQuery(query)
		
		ranges = []
		while ranges_elts.next():
			range_elt = ranges_elts.current()[0]["ran_elt"][1]
			ran = self.i.getImage(range_elt, RDF_TYPE)
			if ran != None:
				ranges.append(ran)
		
		ranges_elts.close()
		return ranges
		
############################################################
	# Get the property of specified Ontology
	def getPropertiesOfUri(self, ontology_uri):
		queryTime = None
		compatTime = None
		
		named_properties = []
		
		time = datetime.today()
		query = "SELECT ?prop, ?prop_name, ?domain_name, ?range_name WHERE { GRAPH <" + ontology_uri + "> { ?prop <" + RDF_TYPE + "> <" + RDF_PROPERTY + "> . ?prop <" + RDFS_LABEL + "> ?prop_name . ?prop <" + RDFS_DOMAIN + "> ?domain . ?domain <" + RDFS_LABEL + "> ?domain_name . ?prop <" + RDFS_RANGE + "> ?range . ?range <" + RDFS_LABEL + "> ?range_name} }"

		properties = self.i.executeQuery(query)
		bindings = properties.bindingNames()

		if queryTime == None:
			queryTime = datetime.today() - time
		else:
			queryTime += datetime.today() - time
			
		time = datetime.today()
		while properties.next():
			prop_set = map(lambda elt: properties.current()[0][elt][1], bindings)
			#prop_name = properties.current()[0]["prop_name"][1]
			#print prop_name
			named_properties.append(prop_set)
		
		#Sort the properties Array
		named_properties.sort()

		if compatTime == None:
			compatTime = datetime.today() - time
		else:
			compatTime += datetime.today() - time
		properties.close()
			
		logger.debug("getPropertiesOfUri:")
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tcompatTime = "+str(compatTime))
		
		return named_properties
		
	# Get the Classes of specified Ontology
	def getClassesOfUri(self, ontology_uri):
		queryTime = None
		compatTime = None
		
		named_classes = []
		
		time = datetime.today()
		query = "SELECT ?class, ?class_name, ?subclass_name WHERE { GRAPH <" + ontology_uri + "> { ?class <" + RDF_TYPE + "> <" + RDF_CLASS + "> . ?class <" + RDFS_LABEL + "> ?class_name . ?class <" + RDFS_SUBCLASSOF + "> ?subclass . ?subclass <" + RDFS_LABEL + "> ?subclass_name} }"

		classes = self.i.executeQuery(query)
		bindings = classes.bindingNames()

		if queryTime == None:
			queryTime = datetime.today() - time
		else:
			queryTime += datetime.today() - time
			
		time = datetime.today()
		while classes.next():
			#class_name = classes.current()[0]["class_name"][1]
			class_set = map(lambda elt: classes.current()[0][elt][1], bindings)
			#print class_name
			named_classes.append(class_set)

		#Sort the Class Array
		named_classes.sort()

		if compatTime == None:
			compatTime = datetime.today() - time
		else:
			compatTime += datetime.today() - time
		classes.close()
			
		logger.debug("getClassesOfUri:")
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tcompatTime = "+str(compatTime))
		
		return named_classes
	
	# domain_type_uri and range_type_uri must be both None OR both valid types.
	def getPropertiesArray(self, ontologies_uris, domain_type_uri, range_type_uri):
		
		queryTime = None
		compatTime = None
		
		properties_res = []
		named_properties = []
		
		for ontology_uri in ontologies_uris:
			
			time = datetime.today()
			query = "SELECT ?prop ?prop_name WHERE { GRAPH <" + ontology_uri + "> { ?prop <" + RDF_TYPE + "> <" + RDF_PROPERTY + "> . ?prop <" + RDFS_LABEL + "> ?prop_name } }"
			properties = self.i.executeQuery(query)
			if queryTime == None:
				queryTime = datetime.today() - time
			else:
				queryTime += datetime.today() - time
			
			time = datetime.today()
			while properties.next():
				property = properties.current()[0]["prop"][1]
				prop_name = properties.current()[0]["prop_name"][1]
				if domain_type_uri != None: # Let us check the given types
					compatible = False
					domains = self.getDomainsArray(property)
					for dom in domains:
						if self.i.derivesFrom(domain_type_uri, dom):
							compatible = True
							break
					ranges = self.getRangesArray(property)
					for ran in ranges:
						if self.i.derivesFrom(range_type_uri, ran):
							compatible = True
							break # for loop
					if not compatible:
						continue # next element in while loop (no appending)
				
				# whether there are no given types OR (there are given types AND the types of the current property are compatible with these)
				properties_res.append(property)
				named_properties.append( [prop_name, property] )
				
			if compatTime == None:
				compatTime = datetime.today() - time
			else:
				compatTime += datetime.today() - time
			properties.close()
			
		logger.debug("getPropertiesArray:")
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tcompatTime = "+str(compatTime))
		
		return named_properties

############################################################

	# ontologies_uris : set(ontology_uri:string)
	def getAllPropertiesOf(self, ontologies_uris, domain_type_uri=None, range_type_uri=None):
		
		named_properties = self.getPropertiesArray(ontologies_uris, domain_type_uri, range_type_uri)
		
		return named_properties
	
############################################################

	def getPossibleRangeElementsArrayV1(self, property_uri, ontologies):
		
		totTime = datetime.today()
		
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		time = datetime.today()
		query = "SELECT ?elt ?type WHERE { ?elt <" + RDF_TYPE + "> ?type }"
		elts = self.i.executeQuery(query)
		if queryTime == None:
			queryTime = datetime.today() - time
		else:
			queryTime += datetime.today() - time
		
		time = datetime.today()
		compatibilities = {}
		elts_res = set()
		while elts.next():
			elt = elts.current()[0]["elt"][1]
			elt_type = elts.current()[0]["type"][1]
			
			if elt_type in compatibilities: # this type's compatibility has already been calculated
				if compatibilities[elt_type]:
					elts_res.add(elt)
				
			else: # calculate the compatibility for this elt_type
				compatible = False
				for range_type in ranges:
					bool = self.i.derivesFrom(elt_type, range_type)
					if bool:
						compatible = True
						break # for loop
				if compatible:
					compatibilities[elt_type] = True
					#print elt_type + " derives from " + str(ranges)
					elts_res.add(elt)
				else:
					compatibilities[elt_type] = False
					#print elt_type + " does not derive from " + str(ranges)
		
		compatTime = datetime.today() - time
		elts.close()
		
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV1:")
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tcompatTime = "+str(compatTime))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res

############################################################

	def getPossibleRangeElementsArrayV2(self, property_uri, ontologies):
		
		totTime = datetime.today()
		
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		
		# build a list of all accepted types, i.e. all sons of all ranges
		buildTime = datetime.today()
		accepted_types = self.i.getChildrenOf(ranges, RDFS_SUBCLASSOF)
		buildTime = datetime.today() - buildTime
		
		logger.debug(str(len(accepted_types))+" elements in accepted_types.")
		#logger.debug("accepted_types = "+str(accepted_types))
		
		time = datetime.today()
		query = "SELECT ?elt ?type WHERE { ?elt <" + RDF_TYPE + "> ?type }"
		elts = self.i.executeQuery(query)
		if queryTime == None:
			queryTime = datetime.today() - time
		else:
			queryTime += datetime.today() - time
		
		time = datetime.today()
		compatibilities = {}
		elts_res = set()
		while elts.next():
			elt = elts.current()[0]["elt"][1]
			elt_type = elts.current()[0]["type"][1]
			
			try:
				accepted_types.index(elt_type)
				#logger.debug(elt_type+" accepted")
				elts_res.add(elt)
			except Exception, e:
				#logger.debug(elt_type+" not accepted")
				pass
		
		compatTime = datetime.today() - time
		elts.close()
		
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV2:")
		logger.debug("\tbuildTime = "+str(buildTime))
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tcompatTime = "+str(compatTime))
		logger.debug("\tlen(accepted_types) = "+str(len(accepted_types)))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res
			
############################################################

	def getPossibleRangeElementsArrayV3(self, property_uri, ontologies):
		
		totTime = datetime.today()
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		# build a list of all accepted types, i.e. all sons of all ranges
		buildTime = datetime.today()
		accepted_types = self.i.getChildrenOf(ranges, RDFS_SUBCLASSOF)
		buildTime = datetime.today() - buildTime
		
		elts_res = set()
		time = datetime.today()
		
		for accepted_type in accepted_types:
			
			query = "SELECT ?elt WHERE { ?elt <" + RDF_TYPE + "> <" + accepted_type + "> }"
			elts = self.i.executeQuery(query)
			
			# add the results in named_elements
			while elts.next():
				elt = elts.current()[0]["elt"][1]
				elts_res.add(elt)
				
			elts.close()
		
		queryTime = datetime.today() - time
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV3:")
		logger.debug("\tbuildTime = "+str(buildTime))
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tlen(accepted_types) = "+str(len(accepted_types)))
		timePerQuery = queryTime / len(accepted_types)
		logger.debug("\ttimePerQuery = "+str(timePerQuery))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res
		
############################################################

	def getPossibleRangeElementsArrayV3b(self, property_uri, ontologies):
			
		totTime = datetime.today()
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		# build a list of all accepted types, i.e. all sons of all ranges
		buildTime = datetime.today()
		accepted_types = self.i.getChildrenOf(ranges, RDFS_SUBCLASSOF)
		buildTime = datetime.today() - buildTime
		
		elts_res = set()
		time = datetime.today()
		
		query = "SELECT ?elt WHERE { ?elt <" + RDF_TYPE + "> ?type FILTER ("
		
		for accepted_type in accepted_types:
			query += "(str(?type) = \"" + accepted_type + "\") || "
		
		query += "false ) }"
		logger.debug("query = "+query)
		elts = self.i.executeQuery(query)
		
		# add the results in named_elements
		while elts.next():
			elt = elts.current()[0]["elt"][1]
			elts_res.add(elt)
		
		elts.close()
		
		queryTime = datetime.today() - time
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV3b:")
		logger.debug("\tbuildTime = "+str(buildTime))
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tlen(accepted_types) = "+str(len(accepted_types)))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res

############################################################

	# takes the ontologies into account.
	def getPossibleRangeElementsArrayV4(self, property_uri, ontologies):
		
		totTime = datetime.today()
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		# build a list of all accepted types, i.e. all sons of all ranges
		buildTime = datetime.today()
		accepted_types = self.i.getChildrenOf(ranges, RDFS_SUBCLASSOF, ontologies)
		buildTime = datetime.today() - buildTime
		
		elts_res = set()
		time = datetime.today()
		
		for accepted_type in accepted_types:
			
			query = "SELECT ?elt WHERE { GRAPH ?onto { ?elt <" + RDF_TYPE + "> <" + accepted_type + "> } FILTER (%s) }" % bool_expr("onto", ontologies)
			elts = self.i.executeQuery(query)
			
			# add the results in named_elements
			while elts.next():
				elt = elts.current()[0]["elt"][1]
				elts_res.add(elt)
			elts.close()
		
		queryTime = datetime.today() - time
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV4:")
		logger.debug("\tbuildTime = "+str(buildTime))
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tlen(accepted_types) = "+str(len(accepted_types)))
		timePerQuery = queryTime / len(accepted_types)
		logger.debug("\ttimePerQuery = "+str(timePerQuery))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res
		
############################################################

	# takes the ontologies into account.
	def getPossibleRangeElementsArrayV4b(self, property_uri, ontologies):
		
		totTime = datetime.today()
		queryTime = None
		
		ranges = self.getRangesArray(property_uri)
		
		if ranges == []:
			logger.info("No \"official\" range found for \"" + property_uri + "\", getting the usage ranges.")
			ranges = self.getUsageRangesArray(property_uri)
			if ranges == []:
				logger.info("No \"usage\" range found for \"" + property_uri + "\", displaying the pimo:Tings found in database.")
				ranges = [PIMO_THING]
		
		# build a list of all accepted types, i.e. all sons of all ranges
		buildTime = datetime.today()
		accepted_types = self.i.getChildrenOf(ranges, RDFS_SUBCLASSOF, ontologies)
		buildTime = datetime.today() - buildTime
		
		elts_res = set()
		time = datetime.today()
		
		query = "SELECT ?elt WHERE { GRAPH ?onto { ?elt <%s> ?type FILTER ((%s) && (%s)) } }" % (RDF_TYPE, bool_expr("type", accepted_types), bool_expr("onto", ontologies))
		
		logger.debug("query = "+query)
		elts = self.i.executeQuery(query)
		
		# add the results in named_elements
		while elts.next():
			elt = elts.current()[0]["elt"][1]
			elts_res.add(elt)
		
		elts.close()		
		queryTime = datetime.today() - time
		totTime = datetime.today() - totTime
		
		logger.debug("getPossibleRangeElementsArrayV4b:")
		logger.debug("\tbuildTime = "+str(buildTime))
		logger.debug("\tqueryTime = "+str(queryTime))
		logger.debug("\tlen(accepted_types) = "+str(len(accepted_types)))
		logger.debug("\ttotTime = "+str(totTime))
		
		return elts_res
		
############################################################

	def getPossibleRangeElementsFor(self, property_uri, priority_level, ontologies):
		
		if priority_level == 0:
			self.prec_ontologies = set()
			self.prec_elements = set()
		
		if ontologies.issubset(self.prec_ontologies):
			# these ontologies will not provide any new element
			logger.debug("No new ontology at level %s, returning a void set.", priority_level)
			return []
		
		self.prec_ontologies = ontologies
		
		# then get the elements
		getPossibleRangeElementsArray = [self.getPossibleRangeElementsArrayV1, self.getPossibleRangeElementsArrayV2, self.getPossibleRangeElementsArrayV3, self.getPossibleRangeElementsArrayV3b, self.getPossibleRangeElementsArrayV4, self.getPossibleRangeElementsArrayV4b]
		
		""" for multi-testing
		elements = [None, None, None, None, None, None]
		for i in range(len(getPossibleRangeElementsArray)):
			elements[i] = getPossibleRangeElementsArray[i](property_uri, ontologies)
			if len(elements[i]) <= 10:
				logger.debug("elements"+str(i)+" = "+str(elements[i]))
		
		elements = elements[5] # V4b
		"""
		
		elements = self.getPossibleRangeElementsArrayV4b(property_uri, ontologies)
		
		
		time = datetime.today()
		
		# remove every already-known element from the returned elements
		elements -= self.prec_elements
		logger.debug("New elements = %s" % elements)
		logger.debug("Old elements = %s" % self.prec_elements)
		# add every new element in the already-known elements
		self.prec_elements |= elements
		
		setOpsTime = datetime.today() - time
		
		
		time = datetime.today()
		
		named_elements = []
		for elt in elements:
			thing_name = self.getName(elt)
			if thing_name == None:
				thing_name = elt
			named_elements.append([thing_name, elt])
		
		namingTime = datetime.today() - time
		logger.debug("namingTime = %s" % namingTime)
		logger.debug("setOpsTime = %s" % setOpsTime)
		
		#gen = rdf_gen(self.i.results_path+"/othersList.rdf")
		#gen.write_list(["Annotation:item", "Annotation:url"], named_elements)
		
		return named_elements

############################################################

	# returns the ontology containing the given property.
	# not callable through python_calls.
	def getOntologyContaining(self, property_uri):
		
		query = "SELECT * WHERE { GRAPH ?onto " + makeExistenceWhereClause(property_uri, RDF_TYPE, RDF_PROPERTY) + " }"
		logger.debug(query)
		ontologies = self.i.executeQuery(query)
		
		ontology = None
		if ontologies.next():
			ontology = ontologies.current()[0]["onto"][1]
		else:
			ontologies.close()
			return None
		
		if ontologies.next():
			ontology2 = ontologies.current()[0]["onto"][1]
			# there are more than one ontology containing this property!
			ontologies.close()
			raise DatabaseIncoherenceException("The property \"" + property_uri + "\" has been located in more than one ontology (at least \"" + ontology + "\" and \"" + ontology2 + "\").")
		
		ontologies.close()
		return ontology
		
############################################################

	def getThingNameOfResource(self, resource_uri):
		
		query = "SELECT ?thing_uri ?thing_name WHERE { ?thing_uri <" + PIMO_GROUNDINGOCCURRENCE + "> <" + resource_uri + "> . ?thing_uri <" + RDFS_LABEL + "> ?thing_name }"
		things = self.i.executeQuery(query)
		
		thing_name = None
		if things.next():
			thing_name = things.current()[0]["thing_name"][1]
		else:
			thing_name = None
		
		if things.next():
			thing_name2 = things.current()[0]["thing_name"][1]
			things.close()
			raise DatabaseIncoherenceException("The uri \"" + resource_uri + "\" has been located as a groundingOccurrence for more than one pimo:Thing (at least \"" + thing_name + "\" and \"" + thing_name2 + "\").")
		
		things.close()
		return thing_name
		
############################################################

	def getThingNameOfThing(self, thing_uri):
		
		query = "SELECT ?thing_name WHERE { <" + thing_uri + "> <" + RDFS_LABEL + "> ?thing_name }"
		thing_names = self.i.executeQuery(query)
		
		thing_name = None
		if thing_names.next():
			thing_name = thing_names.current()[0]["thing_name"][1]
		else:
			thing_name = None
		
		if thing_names.next():
			thing_name2 = thing_names.current()[0]["thing_name"][1]
			thing_names.close()
			raise DatabaseIncoherenceException("The thing \"" + thing_uri + "\" more than one name/label (at least \"" + thing_name + "\" and \"" + thing_name2 + "\").")
		
		thing_names.close()
		return thing_name

############################################################

	def getName(self, resource_uri):
		
		# the case where an element is the grouding occurrence of a pimo:Document
		document_uri = self.i.getAntecedent(PIMO_GROUNDINGOCCURRENCE, resource_uri)
		if document_uri != None:
			label = self.i.getImage(document_uri, NAO_PREFLABEL)
			if label != None:
				return label
			else:
				raise DatabaseIncoherenceException("The document %s does not have any nao:prefLabel." % document_uri)
		
		# select any "label"
		query = "SELECT ?obj WHERE { <%s> ?rel ?obj FILTER regex(str(?rel), \"label\", 'i') }" % resource_uri
		names = self.i.executeQuery(query)
		
		name = None
		if names.next():
			# Let's take the first result
			name = names.current()[0]["obj"][1]
		
		names.close()
		
		if name != None:
			return name
		
		# give it a short name
		index = resource_uri.rfind("/")
		if index != -1 and len(resource_uri) - index >= 3 :
			name = resource_uri[index+1:len(resource_uri)]
		else:
			index = resource_uri[0:len(resource_uri)-1].rfind("/")
			if index != -1 and len(resource_uri) - 1 - index >= 3 :
				name = resource_uri[index+1:len(resource_uri)-1]
		
		if name == None:
			name = resource_uri
		
		return name


               ##############################               
######################### "GETTERS" ########################
							 ############################## (for a particular resource)


	def getAllPropertiesForResource(self, resource_uri):
		
		query = "SELECT ?rel ?obj WHERE { <%s> ?rel ?obj }" % resource_uri
		relations = self.i.executeQuery(query)
		
		relations_res = set()
		
		while relations.next():
			
			rel = relations.current()[0]["rel"][1]
			obj = relations.current()[0]["obj"][1]
			logger.debug("rel = %s ; obj = %s" % (rel, obj))
			
			relations_res.add( (rel, self.getName(rel), obj, self.getName(obj)) )
		
		relations.close()
		
		return relations_res

	def getRelatedResources(self, property, value):

		if property == NAO_HASTAG:
			query = "SELECT ?source WHERE {<" + value + "> <" + NAO_PREFLABEL + "> ?tag_name . ?pro_id <" + NAO_PREFLABEL + "> ?tag_name . ?pro_id <" + NAO_ISTAGFOR + "> ?source_id . ?source_id <" + PIMO_GROUNDINGOCCURRENCE + "> ?source}"
		else:
			query = "SELECT ?source WHERE {?source_id <" + property  + "> <" + value + ">. ?source_id <" + PIMO_GROUNDINGOCCURRENCE + "> ?source}"
				
		
		resources = self.i.executeQuery(query)
		
		resources_res = set()		
		
		while resources.next():
			resource = resources.current()[0]["source"][1]
			logger.debug("resource = %s" % resource)
			resources_res.add(resource)
		resources.close()
		
		return resources_res
		

               ##############################               
######################### "SETTERS" ########################
							 ##############################               


	# param value - describes the resource : String
	# Currently, we do not take care about any type for the given value, it is registered as a string. Further programming may take this typing in consideration.
	def createDescribingProperty(self, user_id, property_name):
		
		# Create the container NRL_ONTOLOGY (= storing graph = context) if not already existing.
		if self.i.getContextType(DEFAULT_ONTOLOGY_URI) != NRL_ONTOLOGY:
			self.i.createContext(DEFAULT_ONTOLOGY_URI, NRL_ONTOLOGY)
		
		describing_property_uri = makeUriWithUserId(user_id, kill_spaces(property_name))
		
		ontology = self.getOntologyContaining(describing_property_uri)
		if ontology == None: # the property does not exist yet, we have to create it "on the go"
			# "create a new rdfs:Property"
			error_code = self.i.registerStatement(describing_property_uri, RDF_TYPE, RDF_PROPERTY)
			# "a sub-property of pimo:describingProperty"
			error_code = self.i.registerStatement(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
			# "has an rdfs:label"
			error_code = self.i.registerStatement(describing_property_uri, RDFS_LABEL, property_name, XMLSCHEMA_STRING)
		else:
			raise CanNotException("\""+ property_name +"\" already exists as a pimo:DescribingProperty for user \"" + user_id + "\". Can not create it.")
			
		return describing_property_uri
################################# Create a statement ###########################

	def getSubClassArray(self, prop_uri):
		"""get the subclass by Range """
		range_array = self.getRangesArray(prop_uri)
		allClass_res = set()

		if range_array == []:
			logger.info("No \"official\" range found for \"" + prop_uri + "\", getting the usage ranges.")
			range_array = self.getUsageRangesArray(prop_uri)
			if range_array == []:
				logger.info("No \"usage\" range found for \"" + prop_uri + "\", displaying the pimo:Tings found in database.")
				range_array = [PIMO_THING]
		
		for range_uri in range_array:
			allClass_res.add( (range_uri, self.getName(range_uri)) )
			query = "SELECT ?subclass, ?subclass_name WHERE { ?subclass <" + RDFS_SUBCLASSOF + "> <" + range_uri + "> . ?subclass <" + RDFS_LABEL + "> ?subclass_name}"
			logger.debug("query = "+query)
			elts = self.i.executeQuery(query)
		
			while elts.next():
				subclass = elts.current()[0]["subclass"][1]
				subclass_name = elts.current()[0]["subclass_name"][1]
				logger.debug("subclass = %s ; subclass_name = %s" % (subclass, subclass_name))
				allClass_res.add( (subclass, subclass_name) )

		return  allClass_res

	def addStatement(self, class_uri, statement_label, statement_description="", statement_label_language=XMLSCHEMA_STRING):
		""" Add a statement in the database.
			:Parameters:
				`prop_uri` : str
					the prop_uri
				`statement_label` : str
					The string value to be used as a literial name for the  statement.
				`statement_description` : str
					An optional description for that statement.
				`statement_label_language` : str
					An optional language for the statement to be stored. Will very often, if not always, be xmlschema:String (default value).
			:Returns: 0
			:Raises `CanNotException`: if the statement with same name already existed
		"""
		
		# if the statement with same name already exists, we will not do anything
		logger.debug("adding statement with name: %s" % statement_label)
		
		query = "SELECT ?statement, ?statement_name WHERE {?statement <" + RDF_TYPE + "> <" + class_uri + "> . ?statement <" + NAO_PREFLABEL + "> \"" + statement_label + "\"^^<" + XMLSCHEMA_STRING + "> }"

		existing_statement = self.i.executeQuery(query)
		
		if (existing_statement.next()):
			existing_statement.close()
			raise CanNotException("already has a statement named \"" + statement_label + "\".")
		
		existing_statement.close()
		
		# the following points have been written paying attention to the NEPOMUK Annotation Ontology (NAO) standards, which can be found at http://www.semanticdesktop.org/ontologies/2007/08/15/nao/#2.3._Tagging_as_Annotation on the 2008-06-09
		
		# 1. create a new tag id to identify the new nao:Tag
		statement_id = self.i.getUniqueId()
		
		# 2. write the information about that tag in the database: rdf:type, nao:created, nao:prefLabel, nao:description
		
		# 2.1. rdf:type
		#		[tag_id, rdf:type, nao:tag, ?]
		error_code = self.i.registerStatement(statement_id, RDF_TYPE, class_uri)
		# print error_code
		
		# 2.2. nao:created
		#		[tag_id, nao:created, date, ?]
		# Here, the type for the date should be XMLSCHEMA_DATETIME. But when we try to save the statement with this type, the date seems to be converted and appears in an ugly and wrong way (e.g. '-4713-01-01TZ'). Currently we store it as a string.
		date = getDateTime()
		error_code = self.i.registerStatement(statement_id, NAO_CREATED, date, XMLSCHEMA_STRING)
		#print statement
		# print error_code
		
		# 2.3. nao:prefLabel
		#		[tag_id, nao:prefLabel, tag_label, ?]
		error_code = self.i.registerStatement(statement_id, NAO_PREFLABEL, statement_label, statement_label_language)
		# print error_code
		
		# 2.4. nao:description
		#		[tag_id, nao:description, tag_description, ?]
		error_code = self.i.registerStatement(statement_id, NAO_DESCRIPTION, statement_description, XMLSCHEMA_STRING)
		# print error_code
		
		return 0

############################################################

	# param value - describes the resource : String
	def deleteDescribingProperty(self, describing_property_uri):
		
		# Check that this property is not used. If it is, do not delete it.
		query = "ASK { ?res <%s> ?obj }" % describing_property_uri
		res = self.i.executeQuery(query)
		b = res.boolValue()
		res.close()
		if b:
			raise CanNotException("Can not delete %s because it is in use." % describing_property_uri)
		# Maybe some implementations could consider asking the user the right to delete this properties uses, and then do it (or not).
		
		
		# Check that the given property is a subclass of DescribingProperty.
		query = "SELECT * WHERE " + makeExistenceWhereClause(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next(): # the given property really exists as a subPropertyOf DescribingProperty
			graphs.close()
			error_code = self.i.deleteStatement(describing_property_uri, RDF_TYPE, RDF_PROPERTY)
			error_code = self.i.deleteStatement(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
			error_code = self.i.deleteAllStatements(describing_property_uri, RDFS_LABEL, "?")
		else:
			graphs.close()
			raise CanNotException("\""+ describing_property_uri +"\" does not exist as a pimo:DescribingProperty. Can not delete it.")
		
		return 0
		
############################################################

	# param value - describes the resource : String
	def isDescribingProperty(self, property_uri):
		
		# Check that the given property is a subclass of DescribingProperty.
		query = "SELECT * WHERE " + makeExistenceWhereClause(property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next(): # the given property exists as a subPropertyOf DescribingProperty
			graphs.close()
			return True
		else:
			graphs.close()
			return False
		
############################################################

	def setValuedDescription(self, thing_uri, describing_property_uri, value):
		
		# as explained at http://www.dfki.uni-kl.de/~sauermann/2006/01-pimo-report/pimOntologyLanguageReport.html#ch-Representing%20Things (available on the 2008-06-17): 
		# "The user creates describing properties on the go. If a new property is needed, it is instantly created. There is no strong domain/range typing, if the user wants to use the property in an "unintended" way, the user is always right to do so."
		
		logger.debug("setValuedDescription, thing_uri = " + thing_uri)
		logger.debug("setValuedDescription, describing_property_uri = " + describing_property_uri)
		
		# Check that thing_uri really is a pimo:Thing
		if not self.i.instanceOf(thing_uri, PIMO_THING):
			raise TypeError("Can not add a describing property to \"" + thing_uri + "\" because it is not an instance of pimo:Thing.")
		
		# Check that describing_property_uri is a subPropertyOf DescribingProperty
		query = "SELECT * WHERE " + makeExistenceWhereClause(describing_property_uri, RDFS_SUBPROPERTYOF, PIMO_DESCRIBINGPROPERTY)
		graphs = self.i.executeQuery(query)
		
		if graphs.next():
			logger.debug(graphs.current()[0]["relation"][1])
			graphs.close()
		else:
			graphs.close()
			raise CanNotException("\""+ describing_property_uri +"\" is not a pimo:DescribingProperty. You have to create that property before you use it.")
		
		error_code = self.i.setImage(thing_uri, describing_property_uri, value, XMLSCHEMA_STRING)
		
		return error_code
		
############################################################

	def removeValuedDescription(self, thing_uri, describing_property_uri, value):
		
		# remove the binding between the resource and the value
		error_code = self.i.deleteStatement(thing_uri, describing_property_uri, value, XMLSCHEMA_STRING)
		
		return error_code
	
############################################################

	# @param super_property the parent property of the property given.
	def createProperty(self, user_id, property_name, super_property_uri, inverse_property_name, domain_uri=None, range_uri=None):
		
		# Create the container Ontology (= storing graph = context) if not already existing.
		if self.i.getContextType(DEFAULT_ONTOLOGY_URI) != NRL_ONTOLOGY:
			self.i.createContext(DEFAULT_ONTOLOGY_URI, NRL_ONTOLOGY)
		
		try:
			[PIMO_PARTOF, PIMO_HASPART, PIMO_HASTOPIC, PIMO_ISTOPICOF, PIMO_RELATED].index(super_property_uri)
		except:
			raise CanNotException("The super property given is not a valid super property. Can not create property.")
		
		property_uri = makeUriWithUserId(user_id, kill_spaces(property_name))
		inverse_property_uri = makeUriWithUserId(user_id, kill_spaces(inverse_property_name))
		
		# check that the property_uri is not taken yet
		ontology = self.getOntologyContaining(property_uri)
		if ontology != None: # that property already exists!
			raise CanNotException("The property \"" + property_name + "\" already exists for user \"" + user_id + "\". Can not create it.")
		
		# check that the inverse_property_uri is not taken yet
		ontology = self.getOntologyContaining(inverse_property_uri)
		if ontology != None: # that property already exists!
			raise CanNotException("The inverse property \"" + inverse_property_name + "\" already exists for user \"" + user_id + "\". Can not create it.")
		
		# Let us create that property
		# paul:supervises - rdf:type - rdf:Property
		error_code = self.i.registerStatement(property_uri, RDF_TYPE, RDF_PROPERTY)
		# paul:supervises - rdfs:subPropertyOf - pimo:related
		error_code = self.i.registerStatement(property_uri, RDFS_SUBPROPERTYOF, super_property_uri)
		# paul:supervises - rdfs:label - "supervises"
		error_code = self.i.registerStatement(property_uri, RDFS_LABEL, property_name, XMLSCHEMA_STRING)
		
		# paul:supervises - protege:inverseProperty - paul:isSupervisedBy
		error_code = self.i.registerStatement(property_uri, PROTEGE_INVERSEPROPERTY, inverse_property_uri)
		# paul:isSupervisedBy- protege:inverseProperty - paul:supervises
		error_code = self.i.registerStatement(inverse_property_uri, PROTEGE_INVERSEPROPERTY, property_uri)
		# paul:isSupervisedBy - rdf:type - rdf:Property
		error_code = self.i.registerStatement(inverse_property_uri, RDF_TYPE, RDF_PROPERTY)
		# paul:isSupervisedBy - rdfs:subPropertyOf - pimo:related
		error_code = self.i.registerStatement(inverse_property_uri, RDFS_SUBPROPERTYOF, INVERSE.get(super_property_uri))
		# paul:isSupervisedBy - rdfs:label - "is supervised by"
		error_code = self.i.registerStatement(inverse_property_uri, RDFS_LABEL, inverse_property_name, XMLSCHEMA_STRING)
		
		# Register the domain & range types if they have been given.
		if domain_uri != None and range_uri != None:
			logger.debug("registering types")
			error_code = self.i.registerStatement(property_uri, RDFS_DOMAIN, domain_uri)
			error_code = self.i.registerStatement(property_uri, RDFS_RANGE, range_uri)
			error_code = self.i.registerStatement(inverse_property_uri, RDFS_DOMAIN, range_uri)
			error_code = self.i.registerStatement(inverse_property_uri, RDFS_RANGE, domain_uri)
		
		return property_uri
		
############################################################

	def deleteProperty(self, property_uri):
		
		# Check that this property is not used. If it is, do not delete it.
		query = "ASK { ?res <%s> ?obj }" % property_uri
		res = self.i.executeQuery(query)
		b = res.boolValue()
		res.close()
		if b:
			raise CanNotException("Can not delete %s because it is in use." % property_uri)
		
		# Check that this property has not any subproperty.
		query = "ASK { ?res <%s> <%s> }" % (RDFS_SUBPROPERTYOF, property_uri)
		res = self.i.executeQuery(query)
		b = res.boolValue()
		res.close()
		if b:
			raise CanNotException("Can not delete %s because it has some subproperties." % property_uri)
		
		# Check that the given property has no child property.
		query = "SELECT ?child WHERE { ?child <" + RDFS_SUBPROPERTYOF + "> <" + property_uri + "> }"
		children = self.i.executeQuery(query)
		
		if children.next():
			child1 = children.current[0]["child"][1]
			children.close()
			raise CanNotException("You can not delete the property \"" + property_uri + "\" because it has at least one child property: " + child1 + ".")
		
		children.close()
		
		# Check that this property exists
		#query = "SELECT * { <" + property_uri + "> <" + RDF_TYPE + "> <" + RDF_PROPERTY + "> }"
		#props = self.i.executeQuery(query)
		#if not props.next():
			#raise CanNotException("The property \"" + property_uri + "\" does not exist.")
		#props.close()
		
		# Let us remove that property
		error_code = self.i.deleteStatement(property_uri, RDF_TYPE, RDF_PROPERTY)
		error_code = self.i.deleteAllStatements(property_uri, RDFS_SUBPROPERTYOF, "?")
		error_code = self.i.deleteAllStatements(property_uri, RDFS_LABEL, "?")
		
		inverse_property_uri = self.i.getPimosInversePropertyOf(property_uri)
		logger.debug("inverse_property_uri = %s" % inverse_property_uri)
		
		if inverse_property_uri != None:
			error_code = self.i.deleteStatement(inverse_property_uri, RDF_TYPE, RDF_PROPERTY)
			error_code = self.i.deleteAllStatements(inverse_property_uri, RDFS_SUBPROPERTYOF, "?")
			error_code = self.i.deleteAllStatements(inverse_property_uri, RDFS_LABEL, "?")
			error_code = self.i.deleteStatement(property_uri, PROTEGE_INVERSEPROPERTY, inverse_property_uri)
			error_code = self.i.deleteStatement(inverse_property_uri, PROTEGE_INVERSEPROPERTY, property_uri)
		
		# Delete the registered types if there are some.
		domain_uri = self.i.getImage(property_uri, RDFS_DOMAIN)
		range_uri = self.i.getImage(property_uri, RDFS_RANGE)
		if domain_uri != None and range_uri != None:
			logger.debug("unregistering types")
			error_code = self.i.deleteStatement(property_uri, RDFS_DOMAIN, domain_uri)
			error_code = self.i.deleteStatement(property_uri, RDFS_RANGE, range_uri)
			error_code = self.i.deleteStatement(inverse_property_uri, RDFS_DOMAIN, range_uri)
			error_code = self.i.deleteStatement(inverse_property_uri, RDFS_RANGE, domain_uri)
		
		return 0

############################################################

	# @param resource_uri the URI of the resource(of type Thing: web page, e-mail, file, etc.).
	# @param property_uri the URI of the property.
	# @param object_uri the URI of the object (of type Thing: other web page, e-mail, file, etc.).
	def addRelation(self, thing_uri, property_uri, object_uri):
		
		# check that the resource is (or derives from) a pimo:Thing
		#if not self.i.instanceOf(thing_uri, PIMO_THING):
			#raise TypeError("Can not add a relation concerning \"" + thing_uri + "\" because it is not an instance of pimo:Thing.")
		
		## check that the object is (or derives from) a pimo:Thing
		#if not self.i.instanceOf(object_uri, PIMO_THING):
			#raise TypeError("Can not add a relation concerning \"" + object_uri + "\" because it is not an instance of pimo:Thing.")
		
		ontology = self.getOntologyContaining(property_uri)
		if ontology == None: # that property does not exists
			raise CanNotException("The property \"" + property_uri + "\" does not exists. You have to create a relation before you use it.")
		
		# Let us register the relation between these two Things
		error_code = self.i.registerStatement(thing_uri, property_uri, object_uri)
		
		return error_code
	
############################################################

	# @param resource_uri the URI of the resource(of type Thing: web page, e-mail, file, etc.).
	# @param property_uri the URI of the property.
	# @param object_uri the URI of the object (of type Thing: other web page, e-mail, file, etc.).
	def removeRelation(self, resource_uri, property_uri, object_uri):
		
		# Let us remove the relation between these two Things
		error_code = self.i.deleteStatement(resource_uri, property_uri, object_uri)
		
		return error_code












